﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TVShow.Framework;
using System.Collections;
using System.Reflection;
using System.IO;

namespace TVShow.Framework
{
  public class PluginManager
  {
    static INIFile iniFile;

    static public void LoadPlugins(DisplayManager Display) {
      IPlugin plugin;
      //TestRig testRig = new TestRig();
      //testRig.LoadPlugin(Display, new INIFile());
      //testRig.Show();

      iniFile = new INIFile(Shell.AppPath("PluginManager.ini"));

      PluginInfo[] plugins = FindPlugins(AppPath);
      foreach (PluginInfo p in plugins) {
        if (OkayToLoad(p)) {
          System.Diagnostics.Debug.WriteLine("Plugin: " + p.ClassName);
          plugin = CreateInstance(p);
          plugin.LoadPlugin(Display);
        }
      }
    }

    const string CategoryName = "LoadPlugins";
    static bool OkayToLoad(PluginInfo p) {
      string loadPlug = iniFile.GetString(CategoryName, p.ClassName, "").ToLower();
      if (loadPlug == "") {
        iniFile.SaveString(CategoryName, p.ClassName, "yes");
        return true;
      }

      if (loadPlug == "yes")
        return true;

      return false;
    }

    static string AppPath {
      get {
        return Path.GetDirectoryName(System.Windows.Forms.Application.ExecutablePath);
      }
    }

    public static string INIPath(string PluginName) {
      string ret = System.IO.Path.Combine(AppPath, PluginName + ".ini");
      return ret;
    }

    public class PluginInfo
    {
      public string AssemblyPath;
      public string ClassName;
    }

    const string InterfaceName = "TVShow.Framework.IPlugin";
    public static PluginInfo[] FindPlugins(string strPath) {
      ArrayList Plugins = new ArrayList();
      String[] DLLFiles;
      Assembly DLLFile;

      //Go through all DLLs in the directory, attempting to load them
      DLLFiles = Directory.GetFileSystemEntries(strPath, "TVShow.*.dll");
      for (int i = 0; i < DLLFiles.Length; i++) {
        try {
          DLLFile = Assembly.LoadFrom(DLLFiles[i]);
          ExamineAssembly(DLLFile, Plugins);
        }
        catch (Exception ex) {
          System.Diagnostics.Debug.WriteLine("Exception: {0}", ex.Message);
          //Error loading DLL, we don't need to do anything special
        }
      }

      //Return all plugins found
      PluginInfo[] Results = new PluginInfo[Plugins.Count];

      if (Plugins.Count != 0) {
        Plugins.CopyTo(Results);
        return Results;
      }
      return null;
    }

    static void ExamineAssembly(Assembly objDLL, ArrayList Plugins) {
      Type InterfaceType;
      PluginInfo Plugin;

      //Loop through each type in the DLL
      foreach (Type objType in objDLL.GetTypes()) {
        //Only look at public types
        if (objType.IsPublic) {
          //Ignore abstract classes
          if (!((objType.Attributes & TypeAttributes.Abstract) == TypeAttributes.Abstract)) {

            //See if this type implements our interface
            InterfaceType = objType.GetInterface(InterfaceName, true);
            if (InterfaceType != null) {
              //It does
              Plugin = new PluginInfo();
              Plugin.AssemblyPath = objDLL.Location;
              Plugin.ClassName = objType.FullName;
              Plugins.Add(Plugin);
            }
          }
        }
      }
    }

    public static IPlugin CreateInstance(PluginInfo Plugin) {
      Assembly DLLFile;
      IPlugin PluginObj;

      try {
        //Load dll
        DLLFile = Assembly.LoadFrom(Plugin.AssemblyPath);
        //Create and return class instance
        PluginObj = (IPlugin)DLLFile.CreateInstance(Plugin.ClassName);
      }
      catch (Exception ex) {
        System.Diagnostics.Debug.WriteLine("Exception: {0}", ex.Message);
        return null;
      }
      return PluginObj;
    }
  }
}
